package com.xy6.jvm.thread.plate;

import java.util.ArrayList;
import java.util.List;

/**
 * 生产者/消费者模式 示例
 * <pre>
 * 生产者/消费者模式其实是一种很经典的线程同步模型，很多时候，并不是光保证多个线程对某共享资源操作的互斥性就够了，
 * 往往多个线程之间都是有协作的。
 * 
 * 假设有这样一种情况，有一个桌子，桌子上面有一个盘子，盘子里只能放一颗鸡蛋，
 * A专门往盘子里放鸡蛋，如果盘子里有鸡蛋，则一直等到盘子里没鸡蛋，
 * B专门从盘子里拿鸡蛋，如果盘子里没鸡蛋，则等待直到盘子里有鸡蛋。
 * 其实盘子就是一个互斥区，每次往盘子放鸡蛋应该都是互斥的，A的等待其实就是主动放弃锁，B 等待时还要提醒A放鸡蛋。
 * 如何让线程主动释放锁？
 * 很简单，调用锁的wait()方法就好。wait方法是从Object来的，所以任意对象都有这个方法。
 * </pre>
 * 
 * @author zhang
 * @since 2018-05-01
 */
public class Plate {

	List<Object> eggs = new ArrayList<Object>();

	public synchronized Object getEgg() {
		System.out.println("拿到鸡蛋 开始");

		while (eggs.size() == 0) {

			try {
				System.out.println("拿到鸡蛋 等待");

				wait();

			} catch (InterruptedException e) {

			}

		}

		Object egg = eggs.get(0);

		eggs.clear();// 清空盘子

		notify();// 唤醒阻塞队列的某线程到就绪队列

		System.out.println("拿到鸡蛋 结束");

		return egg;

	}

	public synchronized void putEgg(Object egg) {
		System.out.println("放入鸡蛋 开始");

		while (eggs.size() > 0) {

			try {
				System.out.println("放入鸡蛋  等待");

				wait();

			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		}

		eggs.add(egg);// 往盘子里放鸡蛋

		notify();// 唤醒阻塞队列的某线程到就绪队列

		System.out.println("放入鸡蛋 结束");

	}

	static class AddThread extends Thread {

		private Plate plate;

		private Object egg = new Object();

		public AddThread(Plate plate) {

			this.plate = plate;

		}

		public void run() {

			for (int i = 0; i < 5; i++) {

				plate.putEgg(egg);

			}

		}

	}

	static class GetThread extends Thread {

		private Plate plate;

		public GetThread(Plate plate) {

			this.plate = plate;

		}

		public void run() {

			for (int i = 0; i < 5; i++) {

				plate.getEgg();

			}

		}

	}

	public static void main(String args[]) {

		try {

			Plate plate = new Plate();

			Thread add = new Thread(new AddThread(plate));

			Thread get = new Thread(new GetThread(plate));

			add.start();

			get.start();

			add.join();

			get.join();

		} catch (InterruptedException e) {

			e.printStackTrace();

		}

		System.out.println("测试结束");

	}
}
